/**
 * let
 */

// let声明的变量，只在let命令所在的代码块内有效
// {
//   let a = 10;
//   var b = 1;
// }
// a ReferenceError: a is not defined.
// b 1

// let 在 for 循环中的表现 ---------------------------------------------------

// for (var i = 0; i < 3; i++) {
//   // var声明的变量在全局范围内都有效，内层的i值会覆盖外层的i值
//   for (var i = 0; i < 3; i++) {
//     console.log(i)
//   }
//   console.log('内层结束 i = ' + i) // 3
// }
// console.log('循环结束 i = ' + i) // 4

// for (var i = 0; i < 3; i++) {
//   // let形成块级作用域，不会影响到外层的i值
//   for (let i = 0; i < 3; i++) {
//     console.log(i)
//   }
//   console.log('内层结束 i = ' + i)
// }

// let 应用场景：循环绑定事件，事件处理函数中获取正确索引 -----------------------------------------------------

// 循环结束后i值变为3,事件处理函数中的i沿作用域链找到3
// var elements = [{}, {}, {}]
// for (var i = 0; i < elements.length; i++) {
//   elements[i].onclick = function () {
//     console.log(i)
//   }
// }
// elements[2].onclick() // 3

// 用var声明计数器变量循环时，可采用闭包的形式来解决此问题，
// 循环时每个立即执行函数接收到的i参数会保存到函数执行环境的变量对象内，return的函数执行时沿作用域链获取到该变量对象内的i
// var elements = [{}, {}, {}]
// for (var i = 0; i < elements.length; i++) {
//   elements[i].onclick = (function (i) {
//     return function () {
//       console.log(i)
//     }
//   })(i)
// }
// elements[0].onclick()

// 有let之后，let可直接生成块级作用域；保证函数执行环境的变量对象内的i都是独立的
// var elements = [{}, {}, {}]
// for (let i = 0; i < elements.length; i++) {
//   elements[i].onclick = function () {
//     console.log(i)
//   }
// }
// elements[0].onclick()

// for 循环会产生两层作用域 ----------------------------------

// for (let i = 0; i < 3; i++) {
//   let i = 'foo'
//   console.log(i)
// }

// let i = 0

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++


// let 修复了变量声明提升现象，必须先声明后使用 --------------------------------------------

// console.log(foo)
// var foo = 'zce'

// console.log(foo)
// let foo = 'zce'


// ​let声明的全局属性在浏览器环境下不会挂载到window上-----------------------------
// var a = 1
// window.a // 1
// let b = 1
// window.b // undefined


// let和const命令在代码块内会产生暂时性死区--------------------
// ES6明确规定，如果区块中存在let和const命令，这个区块对这些命令声明的变量，
// 从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量，就会报错。
// if (true) {
//   // TDZ开始
//   tmp = 'abc'; // ReferenceError
//   console.log(tmp); // ReferenceError

//   let tmp; // TDZ结束
//   console.log(tmp); // undefined

//   tmp = 123;
//   console.log(tmp); // 123
// }


// babel是如何将es6的let转为var的---------------------------
// 转换前
// {
// 	let a = 1
// }
// let a = 2

// 转换后
// {
//   var _a = 1;
// }
// var a = 2;


/**
 * const
 */

// const声明一个只读的常量。一旦声明，常量的值就不能改变。
// const PI = 3.1415;
// PI = 3 // TypeError: Assignment to constant variable.

// const声明的变量不得改变值，这意味着，const一旦声明变量，就必须立即初始化，不能留到以后赋值。
// const foo; // SyntaxError: Missing initializer in const declaration

// const与let相同只在声明所在的块级作用域内有效
// {
//   const PI = 3.14
// }

// const PI = 3.1415

// const命令只是保证变量名指向的地址不变，并不保证该地址的数据不变
// const foo = {};
// foo.prop = 123;

// foo = {}; // TypeError: Assignment to constant variable.